package com.aboverock.module.system.service.impl;

import com.aboverock.core.spring.SpringContextHolder;
import com.aboverock.module.shiro.service.ShiroService;
import com.aboverock.module.system.constant.SystemConstants;
import com.aboverock.module.system.domain.Resource;
import com.aboverock.module.system.domain.RestUrl;
import com.aboverock.module.system.mapper.ResourceMapper;
import com.aboverock.module.system.service.ResourceService;
import com.aboverock.module.system.service.RoleResourceService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.base.CaseFormat;
import io.swagger.annotations.ApiOperation;
import java.lang.reflect.Modifier;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author Rock Wang
 * @since 2018-04-10
 */
@Service
public class ResourceServiceImpl extends ServiceImpl<ResourceMapper, Resource>
        implements ResourceService {
    private static final String PERMISSION_CONNECTOR = SystemConstants.RESOURCE_PERMISSION_CONNECTOR;

    @Autowired
    private RoleResourceService roleResourceService;

    @Autowired
    ShiroService shiroService;

    @Override
    public String generatePermissionStr(Resource resource) {
        if (isTopResource(resource)) {
            return resource.getCode();
        }

        Resource parentResource = this.getById(resource.getParentId());
        return parentResource.getPermissions() + PERMISSION_CONNECTOR + resource.getCode();
    }

    @Override
    public boolean isDuplicatedCode(Resource resource) {
        if (StringUtils.isBlank(resource.getPermissions())) {
            resource.setPermissions(generatePermissionStr(resource));
        }
        QueryWrapper<Resource> query = new QueryWrapper<Resource>().eq("permissions",
                resource.getPermissions());

        // 如果更新，则排除当前字典的code，判断与其余同级别是否重复
        if (resource.getId() != null) {
            query.ne("id", resource.getId());
        }

        Resource res = this.getOne(query);
        return (null == res) ? false : true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean update(Resource resource, Resource oldResource) throws SQLException {
        if (resource.getCode().equalsIgnoreCase(oldResource.getCode())) {
            return updateById(resource);
        } else {
            StringBuilder replace = new StringBuilder("`permissions`=replace(`permissions`, '")
                    .append(oldResource.getPermissions()).append(PERMISSION_CONNECTOR)
                    .append("', '").append(resource.getPermissions()).append(PERMISSION_CONNECTOR)
                    .append("')");
            UpdateWrapper<Resource> update = new UpdateWrapper<Resource>()
                    .setSql(replace.toString());
            if (updateById(resource) && this.update(new Resource(), update)) {
                shiroService.updatePermission();
                return true;
            } else {
                throw new SQLException("update resource fail");
            }
        }
    }

    /**
     * 根据登陆名查询该用户所拥有的资源.
     *
     * @param loginName the login name
     * @return the resources by login name
     */
    @Override
    public List<Resource> getResourcesByLoginName(String loginName) {
        return baseMapper.selectResourcesByLoginName(loginName);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteById(String resourceId) throws SQLException {
        Resource resource = this.getById(resourceId);
        if (null == resource) {
            return true;
        }

        List<Resource> resources = this.list(new QueryWrapper<Resource>().likeRight("permissions",
                resource.getPermissions() + PERMISSION_CONNECTOR));
        Set<String> resourceIds = new HashSet<>(resources.size() + 1);
        resourceIds.add(resourceId);
        resources.forEach(res -> resourceIds.add(res.getId()));
        
        if (removeByIds(resourceIds) && roleResourceService.removeByResourceIds(resourceIds)) {
            shiroService.updatePermission();
            return true;
        } else {
            throw new SQLException("resource delete failed or has been deleted");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean exchangeOrderNo(Resource resource1, Resource resource2) throws SQLException {
        Long orderNo1 = resource1.getOrderNo();
        Long orderNo2 = resource2.getOrderNo();
        resource1.setOrderNo(orderNo2);
        resource2.setOrderNo(orderNo1);
        if (updateById(resource1) && updateById(resource2)) {
            return true;
        } else {
            throw new SQLException("exchange resource orderNo fail");
        }
    }

    @Override
    public List<Resource> getResources() {
        return this.list(null);
    }
    
    @Override
    public Map<String, List<RestUrl>> getUrls() {
        // 获取url与类和方法的对应信息
        RequestMappingHandlerMapping requestMappingHandlerMapping = SpringContextHolder
                .getBean(RequestMappingHandlerMapping.class);
        Map<RequestMappingInfo, HandlerMethod> handlerMethods = requestMappingHandlerMapping
                .getHandlerMethods();

        List<RestUrl> restUrls = new LinkedList<RestUrl>();
        handlerMethods.forEach((info, method) -> {
            if (Modifier.isAbstract(method.getMethod().getDeclaringClass().getModifiers())
                    || !method.getMethod().getDeclaringClass().getName()
                            .startsWith("com.aboverock")) {
                return;
            }

            PatternsRequestCondition patternsCondition = info.getPatternsCondition();
            String url = patternsCondition.getPatterns().stream().collect(Collectors.joining(","));
            RequestMethodsRequestCondition methodsCondition = info.getMethodsCondition();
            String httpMethod = methodsCondition.getMethods().stream().map(m -> m.toString())
                    .collect(Collectors.joining(","));
            String className = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN,
                    method.getMethod().getDeclaringClass().getSimpleName());

            RestUrl restUrl = new RestUrl();
            restUrl.setUrl(url);
            restUrl.setMethod(httpMethod);
            restUrl.setClassName(className);
            if (method.getMethodAnnotation(ApiOperation.class) != null) {
                restUrl.setDescription(method.getMethodAnnotation(ApiOperation.class).value());
            }
            restUrls.add(restUrl);
        });

        List<String> excludeUrls = Arrays.asList(ShiroService.DEFAULT_FILTER_CHAIN_URL);
        Map<String, List<RestUrl>> collect = restUrls.stream()
                .filter(restUrl -> !excludeUrls.contains(restUrl.getUrl()))
                .collect(Collectors.groupingBy(RestUrl::getClassName));

        return collect;
    }

    /**
     * Checks if is top resource.
     *
     * @param resource the resource
     * @return true, if is top resource
     */
    private boolean isTopResource(Resource resource) {
        return null == resource.getParentId();
    }

}
